#include <libnetwork/share.h>
#include <libnetwork/server.h>

// Internal function definitions
// End internal function definitions

SOCK *server_setup(listen_port, is_ssl, server_cert, server_key)
int listen_port;
bool is_ssl;
char *server_cert, *server_key;
{
 SOCK *_new_socket;
 struct sockaddr_in _my_addr;
 int _yes=1;

 _new_socket = malloc( sizeof(SOCK) * 1 );

 if( _new_socket == NULL ) {
  fprintf(stderr, "Error: libsrvnet.a->server.c->server_setup(): Unable to malloc()\n");
  fflush(stderr);
  exit(1);
 }

 _init_socket_variable(_new_socket);

 _new_socket->_status += STATUS_SRV;

 if ((_new_socket->_sockfd = socket(AF_INET, SOCK_STREAM, 0)) == -1) {
  err("Error: libsrvnet.a->server.c->server_setup(): socket()", _new_socket);
  _new_socket->_status += STATUS_INIT_ERROR;
  return _new_socket;
 }

 if (setsockopt(_new_socket->_sockfd, SOL_SOCKET, SO_REUSEADDR, &_yes, sizeof(int)) == -1) {
  err("Error: libsrvnet.a->server.c->server_setup(): setsockopt()", _new_socket);
  _new_socket->_status += STATUS_INIT_ERROR;
  return _new_socket;
 }

 _my_addr.sin_family = AF_INET;
 _my_addr.sin_port = htons(listen_port);
 _my_addr.sin_addr.s_addr = INADDR_ANY;
 memset(_my_addr.sin_zero, '\0', sizeof _my_addr.sin_zero);

 if (bind(_new_socket->_sockfd, (struct sockaddr *)&_my_addr, sizeof _my_addr) == -1) {
  err("Error: libsrvnet.a->server.c->server_setup(): bind()", _new_socket);
  _new_socket->_status += STATUS_INIT_ERROR;
  return _new_socket;
 }

 if (listen(_new_socket->_sockfd, 10) == -1) {
  err("Error: libsrvnet.a->server.c->server_setup(): listen()", _new_socket);
  _new_socket->_status += STATUS_INIT_ERROR;
  return _new_socket;
 }

 if( is_ssl ) {
  int _x;
  _new_socket->_status += STATUS_ENC;
  if( server_cert == NULL || server_key == NULL ) {
   err("Error: libsrvnet.a->server.c->server_setup(): need PEM cert and key for SSL", _new_socket);
   _new_socket->_status += STATUS_INIT_ERROR;
   return _new_socket;
  }
  SSL_library_init();
  for( _x = 0; server_cert[_x] != '\0'; _x ++ ) {
   _new_socket->cert[_x] = server_cert[_x];
  }
  _new_socket->cert[_x] = '\0';

  for( _x = 0; server_key[_x] != '\0'; _x ++ ) {
   _new_socket->key[_x] = server_key[_x];
  }
  _new_socket->key[_x] = '\0';
 }

 _new_socket->_status += STATUS_INIT; 
 _new_socket->_status += STATUS_MAIN_SRV;

 return _new_socket;
}

SOCK *server_accept_connections(main_socket)
SOCK *main_socket;
{
 SOCK *_new_sock;
 socklen_t _sin_size = sizeof(_new_sock->_their_addr);

 _new_sock = malloc(sizeof(SOCK) * 1);

 if( _new_sock == NULL ) {
  fprintf(stderr, "Error: libsrvnet.a->server.c->server_accept_connections(): Unable to malloc()\n");
  fflush(stderr);
  exit(1);
 }

 _init_socket_variable(_new_sock);

 _new_sock->_status += STATUS_SRV;

 if( main_socket->_status&STATUS_PTHREAD ) {
  _new_sock->_status += STATUS_PTHREAD;
 }

 if ((_new_sock->_sockfd = accept(main_socket->_sockfd, (struct sockaddr *)&_new_sock->_their_addr, &_sin_size)) == -1) {
  err("Error: libsrvnet.a->server.c->server_accept_connections(): failed to accept()", _new_sock);
  _new_sock->_status += STATUS_INIT_ERROR;
  return _new_sock;
 }

 if( main_socket->_status&STATUS_ENC ) {
  _new_sock->_status += STATUS_ENC;

  _new_sock->_ssl_ctx = SSL_CTX_new(SSLv2_method());

  if( _new_sock->_ssl_ctx == NULL ) {
   err("Error: libsrvnet.a->server.c->server_accept_connections(): failed to SSL_CTX_new()", _new_sock);
   _new_sock->_status += STATUS_INIT_ERROR;
   return _new_sock;
  }

  if( SSL_CTX_use_certificate_file(_new_sock->_ssl_ctx, main_socket->cert, SSL_FILETYPE_PEM) != 1 ) {
   err("Error: libsrvnet.a->server.c->server_accept_connections(): failed to SSL_CTX_use_certificate_file()", _new_sock);
   _new_sock->_status += STATUS_INIT_ERROR;
   ERR_print_errors_fp(stderr);
   return _new_sock;
  }

  if( SSL_CTX_use_PrivateKey_file(_new_sock->_ssl_ctx, main_socket->key, SSL_FILETYPE_PEM) != 1 ) {
   err("Error: libsrvnet.a->server.c->server_accept_connections(): failed to SSL_CTX_use_PrivateKey_file()", _new_sock);
   _new_sock->_status += STATUS_INIT_ERROR;
   return _new_sock;
  }

  _new_sock->_ssl = SSL_new(_new_sock->_ssl_ctx);

  if( SSL_set_fd(_new_sock->_ssl, _new_sock->_sockfd) == 0 ) {
   err("Error: libsrvnet.a->server.c->server_accept_connections(): failed to SSL_set_fd()", _new_sock);
   return _new_sock;
  }

  int ret=SSL_accept(_new_sock->_ssl);
  if( ret != 1 ) {
   if( SSL_get_error(_new_sock->_ssl, ret) == SSL_ERROR_SSL) {
    ERR_print_errors_fp(stdout);
   }
  }
 }

 _new_sock->_status += STATUS_INIT;
 _new_sock->_status += STATUS_CONN;

 return _new_sock;
}

